/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package elaborate.h2x.transform.service;

import elaborate.h2x.transform.TransformingModel;
import elaborate.h2x.transform.TransformingNode;
import elaborate.h2x.transform.TransformingNodeId;
import java.io.File;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 *
 * @author lendle
 */
public class DefaultLocatorImpl implements Locator{

    public TransformingNode locateElementById(TransformingModel model, String id) {
        return model.getNode(TransformingNodeId.fromString(id));
    }

    public TransformingNode locateElementByQName(TransformingModel model, TransformingNode contextNode, QName qName) {
        return this.locateElementByQName(model, contextNode, qName, 0);
    }

    public TransformingNode locateElementByQName(TransformingModel model, TransformingNode contextNode, QName qName, int index) {
        try{
            XPath xpath=XPathFactory.newInstance().newXPath();
            Element originalElement=(Element) contextNode.getOriginalNode();
            Node targetNode=null;
            if(qName.getNamespaceURI()==null){
                targetNode=(Node) xpath.evaluate("child::*[local-name()='"+qName.getLocalPart()+"'][position()="+(index+1)+"]", originalElement, XPathConstants.NODE);
            }
            else{
                targetNode=(Node) xpath.evaluate("child::*[namespace-uri()='"+qName.getNamespaceURI()+"' and local-name()='"+qName.getLocalPart()+"'][position()="+(index+1)+"]", originalElement, XPathConstants.NODE);
            }
            if(targetNode!=null){
                return model.getNode(targetNode);
            }
        }catch(Exception e){
        }
        return null;
    }

    public TransformingNode locateAttrNode(TransformingModel model, TransformingNode contextNode, QName attribute) {
        try{
            XPath xpath=XPathFactory.newInstance().newXPath();
            Element originalElement=(Element) contextNode.getOriginalNode();
            Node targetNode=null;
            if(attribute.getNamespaceURI()==null){
                targetNode=(Node) xpath.evaluate("attribute::*[local-name()='"+attribute.getLocalPart()+"']", originalElement, XPathConstants.NODE);
            }
            else{
                targetNode=(Node) xpath.evaluate("attribute::*[namespace-uri()='"+attribute.getNamespaceURI()+"' and local-name()='"+attribute.getLocalPart()+"']", originalElement, XPathConstants.NODE);
            }
            if(targetNode!=null){
                return model.getNode(targetNode);
            }
        }catch(Exception e){
        }
        return null;
    }

    public TransformingNode locateAttrNode(TransformingModel model, TransformingNode contextNode, String attribute) {
        return this.locateAttrNode(model, contextNode, new QName(null, attribute));
    }

    public TransformingNode locateTextNode(TransformingModel model, TransformingNode contextNode) {
        return this.locateTextNode(model, contextNode, 0);
    }

    public TransformingNode locateTextNode(TransformingModel model, TransformingNode contextNode, int index) {
        try{
            XPath xpath=XPathFactory.newInstance().newXPath();
            Element originalElement=(Element) contextNode.getOriginalNode();
            Node targetNode=null;
            //System.out.println("exp: "+"child::text()[position()="+(index+1)+"]");
            targetNode=(Node) xpath.evaluate("child::text()[position()="+(index+1)+"]", originalElement, XPathConstants.NODE);
            //System.out.println(targetNode);
            if(targetNode!=null){
                return model.getNode(targetNode);
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public TransformingNode locateNodeByXPath(TransformingModel model, TransformingNode contextNode, String _xpath) {
        try{
            XPath xpath=XPathFactory.newInstance().newXPath();
            Node originalNode= contextNode.getOriginalNode();
            Node targetNode=null;
            targetNode=(Node) xpath.evaluate(_xpath, originalNode, XPathConstants.NODE);
            if(targetNode!=null){
                return model.getNode(targetNode);
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public TransformingNode locateNode(TransformingModel model, TransformingNode contextNode, String expression) {
        expression=expression.trim();
        if(expression.equals(".")){
            return contextNode;
        }
        else if(expression.startsWith("id(")){
            //find by id
            int index=expression.lastIndexOf(")");
            return this.locateElementById(model, expression.substring(3, index));
        }
        else if(expression.startsWith("{")){
            //find by QName
            String qNameExpression=expression.endsWith("]")?expression.substring(0, expression.lastIndexOf("[")):expression;
            QName qName=this.toQName(qNameExpression);
            if(expression.endsWith("]")==false){
                //no position information
                return this.locateElementByQName(model, contextNode, qName);
            }
            else{
                int index1=expression.lastIndexOf("[");
                int index2=expression.lastIndexOf("]");
                int position=Integer.valueOf(expression.substring(index1+1, index2));
                return this.locateElementByQName(model, contextNode, qName, position);
            }
        }
        else if(expression.startsWith("@")){
            //find attribute node
            return this.locateAttrNode(model, contextNode, this.toQName(expression.substring(1)));
        }
        else if(expression.startsWith("text()")){
            //find text node
            if(expression.endsWith("]")==false){
                //no position information
                return this.locateTextNode(model, contextNode);
            }
            else{
                int index1=expression.lastIndexOf("[");
                int index2=expression.lastIndexOf("]");
                int position=Integer.valueOf(expression.substring(index1+1, index2));
                return this.locateTextNode(model, contextNode, position);
            }
        }
        else{
            //xpath
            return this.locateNodeByXPath(model, contextNode, expression);
        }
    }

    private QName toQName(String expression){
        expression=expression.trim();
        int index=expression.lastIndexOf("}");
        String namespaceURI=expression.substring(1, index);
        String localPart=expression.substring(index+1);
        QName qName=(namespaceURI.length()>1)?(new QName(namespaceURI, localPart)):(new QName(localPart));
        return qName;
    }

    public static void main(String [] args) throws Exception{
        DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db=dbf.newDocumentBuilder();
        Document doc=db.parse(new File("example_xml.xml"));
        XPath xpath=XPathFactory.newInstance().newXPath();
        Element element=(Element) xpath.evaluate("/*[local-name()='test1']/*[local-name()='a']/*[local-name()='c']", doc, XPathConstants.NODE);
        System.out.println(element);
        System.out.println(((Node)(xpath.evaluate("child::*[local-name()='b']", element, XPathConstants.NODE))).getFirstChild());
        System.out.println(((Node)(xpath.evaluate("child::*[local-name()='b'][position()=2]", element, XPathConstants.NODE))).getFirstChild());
        System.out.println(((Node)(xpath.evaluate("attribute::*[local-name()='t1']", element, XPathConstants.NODE))));
        System.out.println(((Node)(xpath.evaluate("child::text()[position()=5]", element, XPathConstants.NODE))));
    }
}
